/*
**************************************************************************************
*Myriad Engine                                                                       *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
package leola.backend.interpreter;

import java.util.Stack;

import leola.types.LeoBoolean;
import leola.types.LeoNull;
import leola.types.LeoNumber;
import leola.types.LeoObject;
import leola.types.LeoString;
import leola.types.LeoVoid;

/**
 * The call stack
 * 
 * @author Tony
 *
 */
public class Callstack {

	/**
	 * stack
	 */
	private java.util.Stack<LeoObject> stack;
	
	/**
	 * Stack frames
	 */
	private Stack<StackFrame> stackFrames;
	
	/**
	 * The symbol stack
	 */
	private SymbolStack symbolStack;
	
	public Callstack() {
		this.stack = new Stack<LeoObject>();
		this.stackFrames = new Stack<StackFrame>();
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return this.stack.toString();
	}
	
	/**
	 * @return the current size
	 */
	public int size() {
		return this.stack.size();
	}
	
	/**
	 * Enter in a new {@link Scope}.
	 * 
	 * @param embedded
	 */
	public StackFrame enterScope(boolean embedded) {
		this.symbolStack.push(new Scope(embedded));
		StackFrame frame = new StackFrame(this.stackFrames.size(), this.symbolStack.peek());
		this.stackFrames.push(frame);
		this.stack = frame.getLocalStack(); 
		
		return frame;
	}
	
	/**
	 * Exits a {@link Scope}
	 * @return the scope that was exited
	 */
	public StackFrame exitScope() {
		StackFrame expiredFrame = this.stackFrames.pop();
		this.symbolStack.pop();
		
		if ( ! this.stackFrames.isEmpty() ) {
			StackFrame currentFrame = this.stackFrames.peek();
			this.stack = currentFrame.getLocalStack();
		}
		return expiredFrame;
	}
	
	/**
	 * @return the current {@link StackFrame}
	 */
	public StackFrame getCurrentStackFrame() {
		return this.stackFrames.peek();
	}
	
	/**
	 * @return the symbolStack
	 */
	public SymbolStack getSymbolStack() {
		return symbolStack;
	}
	
	/**
	 * @return true if empty, false otherwise
	 */
	public boolean empty() {
		return this.stack.isEmpty();
	}
	
	/**
	 * @return the object on the stack
	 */
	public LeoObject peek() {
		return this.stack.peek();
	}
	
	/**
	 * Pushes an object
	 * @param obj
	 */
	public void push(LeoObject obj) {
		this.stack.push(obj);
	}
	
	/**
	 * Pushes a boolean
	 * @param bool
	 */
	public void push(boolean bool) {
		this.stack.push(LeoBoolean.get(bool));
	}
	
	/**
	 * Pushes a String 
	 * 
	 * @param str
	 */
	public void push(String str) {
		this.stack.push(new LeoString(str));
	}
	
	/**
	 * Pushes a float
	 * @param v
	 */
	public void push(float v) {
		this.stack.push(new LeoNumber(v));
	}
	
	/**
	 * Pushes an int
	 * @param v
	 */
	public void push(int v) {
		this.stack.push(new LeoNumber(v));
	}

	/**
	 * Pushes an double
	 * @param v
	 */
	public void push(double v) {
		this.stack.push(new LeoNumber(v));
	}
	
	/**
	 * Pushes null
	 */
	public void pushNull() {
		this.stack.push(LeoNull.LEONULL);
	}
	
	/**
	 * Pushes void
	 */
	public void pushVoid() {
		this.stack.push(LeoVoid.LEOVOID);
	}
	
	/**
	 * Pops the {@link LeoObject}
	 * @return
	 */
	public LeoObject pop() {
		return this.stack.pop();
	}
}
